﻿// Inner Kit 库
// XML读取类
// Copyright (c) 2024 by 尔西大帝. All rights reserved.
//
// 这个类用于读取XML，并将其转化为交互者友好的类。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-14

#include "xml_reader.h"
#include <filesystem>
#include <kits.h>
#include <klog.h>
#include <knumber.h>
#include <tinyxml2.h>
#include <windows.h>

using namespace tinyxml2;

namespace ikit {

static const XmlValue EMPTY_VALUE = {0, nullptr, 0, 0.0f};
static const XmlNode EMPTY_NODE = {};
static const std::uint64_t DISABLE_HASH_ID = hash64("Disable");
static const eastl::string EASTL_EMPTY_STRING;
static const eastl::string_view EASTL_EMPTY_STRING_VIEW;
static const sstr EMPTY_STATIC_STRING;

////////////////////////////////////////////////////////////////////////////////
// 通用工具
////////////////////////////////////////////////////////////////////////////////
// 输入字符串，输出相应的16进制；例如：输入"12AB3d"，输出0x12AB3d
static unsigned int hex2uint(const char* buf, int prefix_len, int len) {
  int sum = 0;
  int num = 0;
  char ch = 0;
  for (int i = 0; i < len; i++) {
    // 这里的索引是从第3位开始。因为前两位是0x
    ch = buf[i + prefix_len];

    if (ch >= '0' && ch <= '9') {
      num = ch - '0';
    }

    if (ch >= 'A' && ch <= 'Z') {
      num = ch - 'A' + 10;
    }

    if (ch >= 'a' && ch <= 'z') {
      num = ch - 'a' + 10;
    }

    sum = sum * 16 + num; // 16进制表达
  }
  return sum;
}

// 这里必须严格要求类型，必须是以0x开头。
// 如果成功，返回hex部分的长度，否则返回0
static bool is0xhex(const char* s) {
  if (s == NULL) {
    return false;
  }
  int lower;

  // 必须是3位以上，至少得是0x1这样有三位数
  int len = static_cast<int>(strlen(s));
  if (len < 3) {
    return false;
  }

  // 前两位必须是0x
  if (!(s[0] == '0' && s[1] == 'x')) {
    return false;
  }

  // 这个s[i]真太妙了。原来还能这样！
  for (int i = 2; s[i]; i++) {
    lower = tolower(s[i]);

    bool isNumber = lower >= '0' && lower <= '9';
    bool isHex = lower >= 'a' && lower <= 'f';

    if (!(isNumber || isHex)) {
      return false;
    }
  }

  return true;
}

////////////////////////////////////////////////////////////////////////////////
// XML值
////////////////////////////////////////////////////////////////////////////////

static void auto_set_xml_value(
    XmlValue& item, const char* name, const char* text) {
  // ID为Hash值
  item.id = hash64(name);
  item.value = nullptr;

  // 如果没有字符串，则不处理
  if (text == nullptr) {
    return;
  }

  // 值设置为字符串（需要转码）
  // value = str_converter.UTF8ToGB2312(text);
  // TODO，不知道是否应该在此处就进行转码，可能会导致非常繁重的计算。以后需要改进。
  item.value = str_copy(text);

  // 根据值区别不同的项目
  if (str_equal(text, "true")) {
    item.int_value = 1;
    item.float_value = 1.0f;
  } else if (str_equal(text, "false")) {
    item.int_value = 0;
    item.float_value = 0.0f;
  } else if (is_number(text)) {
    item.int_value = atoi(text);
    item.float_value = static_cast<float>(item.int_value);
  } else if (is_float(text)) {
    item.float_value = static_cast<float>(atof(text));
    item.int_value = static_cast<int>(item.float_value);
  } else if (is0xhex(text)) {
    item.int_value = static_cast<int>(hex_to_uint(text)); // atoi(text);
    item.float_value = static_cast<float>(item.int_value);
  } else {
    item.int_value = 0;
    item.float_value = 0.0f;
  }
}

////////////////////////////////////////////////////////////////////////////////
// XML节点
////////////////////////////////////////////////////////////////////////////////

XmlNode::XmlNode()
    : value_{0, nullptr, 0, 0.0f}
    , attributes_(nullptr)
    , attributes_count_(0)
    , children_(nullptr)
    , children_count_(0) {}

XmlNode::~XmlNode() {
  // 删除值中的字符串
  SAFE_FREE(value_.value);
  // 没有析构函数！手动删除Attribute
  if (attributes_count_ > 0) {
    for (int i = 0; i < attributes_count_; i++) {
      attributes_[i].Release();
    }
  }
  SAFE_DELETE_ARRAY(attributes_);

  SAFE_DELETE_ARRAY(children_);
}

void XmlNode::Read(XMLElement* xml_node) {
  int index = 0;
  // 得到属性
  attributes_count_ = 0;
  for (const XMLAttribute* i = xml_node->FirstAttribute(); i != nullptr;
       i = i->Next()) {
    attributes_count_++;
  }
  if (attributes_count_ > 0) {
    // 分配空间
    attributes_ = new XmlValue[attributes_count_];
    // 依次填充属性
    index = 0;
    for (const XMLAttribute* i = xml_node->FirstAttribute(); i != nullptr;
         i = i->Next()) {
      auto_set_xml_value(attributes_[index], i->Name(), i->Value());
      index++;
    }
  }

  // 得到子项目
  children_count_ = xml_node->ChildElementCount();
  if (children_count_ > 0) {
    // 分配Child空间
    children_ = new XmlNode[children_count_];
    // 将自已的值设置为空（但要设置ID）
    value_ = EMPTY_VALUE;
    value_.id = hash64(xml_node->Name());

    // 依次设置Child
    index = 0;
    for (XMLElement* child = xml_node->FirstChildElement(); child != nullptr;
         child = child->NextSiblingElement()) {
      children_[index].Read(child);
      index++;
    }
  } else {
    // 如果没有Child，则设置自已的值
    children_ = nullptr;
    auto_set_xml_value(value_, xml_node->Name(), xml_node->GetText());
  }
}

const XmlValue& XmlNode::Attr(const char* key) const {
  if (attributes_count_ == 0) {
    return EMPTY_VALUE;
  }
  std::uint64_t hashId = hash64(key);
  for (int i = 0; i < attributes_count_; i++) {
    const XmlValue& item = attributes_[i];
    if (item.id == hashId) {
      return item;
    }
  }
  return EMPTY_VALUE;
}

const XmlValue& XmlNode::operator()(const char* key) const {
  // 没有子项目的情况
  if (children_count_ == 0 && attributes_count_ == 0) {
    return EMPTY_VALUE;
  }
  // 得到HashID
  std::uint64_t hashId = hash64(key);
  // 先检查子项目，再检查属性
  if (children_count_ > 0) {
    for (int i = 0; i < children_count_; i++) {
      if (children_[i].ID() == hashId) {
        return children_[i].value_;
      }
    }
  }
  if (attributes_count_ > 0) {
    for (int i = 0; i < attributes_count_; i++) {
      auto& item = attributes_[i];
      if (item.id == hashId) {
        return item;
      }
    }
  }

  return EMPTY_VALUE;
}

XMFLOAT4 XmlNode::operator()(
    const char* key, const XMFLOAT4 default_value) const {
  auto& item = (*this)(key);
  return item.empty() ? default_value : item;
}

XMFLOAT3 XmlNode::operator()(
    const char* key, const XMFLOAT3 default_value) const {
  auto& item = (*this)(key);
  return item.empty() ? default_value : item;
}
XMVECTOR XmlNode::operator()(
    const char* key, const XMVECTOR default_value) const {
  auto& item = (*this)(key);
  return item.empty() ? default_value : item;
}
XMFLOAT2 XmlNode::operator()(
    const char* key, const XMFLOAT2 default_value) const {
  auto& item = (*this)(key);
  return item.empty() ? default_value : item;
}

XMINT4 XmlNode::operator()(const char* key, const XMINT4 default_value) const {
  auto& item = (*this)(key);
  return item.empty() ? default_value : item;
}

XMINT3 XmlNode::operator()(const char* key, const XMINT3 default_value) const {
  auto& item = (*this)(key);
  return item.empty() ? default_value : item;
}

XMINT2 XmlNode::operator()(const char* key, const XMINT2 default_value) const {
  auto& item = (*this)(key);
  return item.empty() ? default_value : item;
}

Rect XmlNode::operator()(const char* key, const Rect default_value) const {
  auto& item = (*this)(key);
  return item.empty() ? default_value : item;
}

Point XmlNode::operator()(const char* key, const Point default_value) const {
  auto& item = (*this)(key);
  return item.empty() ? default_value : item;
}

Size XmlNode::operator()(const char* key, const Size default_value) const {
  auto& item = (*this)(key);
  return item.empty() ? default_value : item;
}

RectF XmlNode::operator()(const char* key, const RectF default_value) const {
  auto& item = (*this)(key);
  return item.empty() ? default_value : item;
}

PointF XmlNode::operator()(const char* key, const PointF default_value) const {
  auto& item = (*this)(key);
  return item.empty() ? default_value : item;
}

SizeF XmlNode::operator()(const char* key, const SizeF default_value) const {
  auto& item = (*this)(key);
  return item.empty() ? default_value : item;
}

size_t XmlNode::operator()(const char* key, const size_t default_value) const {
  auto& item = (*this)(key);
  return item.empty() ? default_value : item;
}

const bool XmlNode::operator()(const char* key, bool default_value) const {
  auto& item = (*this)(key);
  return item.empty() ? default_value : item;
}

const int XmlNode::operator()(const char* key, int default_value) const {
  auto& item = (*this)(key);
  return item.empty() ? default_value : item;
}

const float XmlNode::operator()(const char* key, float default_value) const {
  auto& item = (*this)(key);
  return item.empty() ? default_value : item;
}

const char* XmlNode::operator()(
    const char* key, const char* default_value) const {
  auto& item = (*this)(key);
  return item.empty() ? default_value : item.value;
}

void XmlNode::Foreach(
    const char* key, std::function<void(XmlNode&, int)> callback) const {
  if (children_count_ == 0) return;

  std::uint64_t hashId = hash64(key);
  int index = 0;
  for (int i = 0; i < children_count_; i++) {
    if (children_[i].ID() == hashId) {
      // 检查Disable，如果Disable存在，则表示此字符无效
      if (children_[i].IsDisable()) {
        continue;
      }
      callback(children_[i], index++);
    }
  }
}

void XmlNode::ForeachPaths(
    const char* key, const std::function<void(const char*)>& callback) const {
  if (children_count_ == 0) return;

  namespace fs = std::filesystem;

  std::uint64_t hashId = hash64(key);
  int index = 0;
  for (int i = 0; i < children_count_; i++) {
    auto& node = children_[i];
    // 检查Disable，如果Disable存在，则表示此字符无效
    if (node.IsDisable() || node.ID() != hashId) {
      continue;
    }

    // 以下为正式处理
    fs::path filePath(node.Text());

    if (!fs::exists(filePath)) {
      KERROR("File %s not exist.", filePath.c_str());
      continue;
    }
    // 检查是否是常规文件
    if (fs::is_regular_file(filePath)) {
      auto fname = filePath.u8string();
      callback((const char*)fname.c_str());
    } else if (fs::is_directory(filePath)) {
      // 遍历目录内容
      for (const auto& entry : fs::directory_iterator(filePath)) {
        auto fname = entry.path().u8string();
        callback((const char*)fname.c_str());
      }
    } else {
      // 既不是文件，也不是目录，不处理
    }
  }
}

int XmlNode::GetCount(const char* key) const {
  if (children_count_ == 0) {
    return 0;
  }
  int count = 0;
  std::uint64_t hashId = hash64(key);
  for (int i = 0; i < children_count_; i++) {
    if (children_[i].ID() == hashId) {
      // 检查Disable，如果Disable存在，则表示此字符无效
      if (children_[i].IsDisable()) {
        continue;
      }
      count++;
    }
  }
  return count;
}

const XmlNode& XmlNode::operator[](const char* key) const {
  if (children_count_ == 0) {
    return EMPTY_NODE;
  }
  std::uint64_t hashId = hash64(key);
  for (int i = 0; i < children_count_; i++) {
    if (children_[i].ID() == hashId) {
      return children_[i];
    }
  }
  return EMPTY_NODE;
}

const XmlNode& XmlNode::Get(const char* key) const {
  if (children_count_ == 0) {
    return EMPTY_NODE;
  }
  auto hash_id = hash64(key);
  for (int i = 0; i < children_count_; i++) {
    if (children_[i].ID() == hash_id) {
      return children_[i];
    }
  }
  return EMPTY_NODE;
}

bool XmlNode::IsDisable() const {
  if (attributes_count_ > 0) {
    for (int i = 0; i < attributes_count_; i++) {
      auto& item = attributes_[i];
      if (item.id == DISABLE_HASH_ID) {
        return item.int_value == 1;
      }
    }
  }
  return false;
}

////////////////////////////////////////////////////////////////////////////////
// XML读取
////////////////////////////////////////////////////////////////////////////////

XmlReader::XmlReader(const char* filename) { Load(filename); }

void XmlReader::Load(const char* filename) {
  assert(strlen(filename) > 0);

  // 创建XML文档对象
  tinyxml2::XMLDocument doc;
  tinyxml2::XMLError error = doc.LoadFile(filename);
  if (error != tinyxml2::XML_SUCCESS) {
    KERROR("Load XML failed: %s, error: %d.", filename, error);
    return;
  }

  // 获取根元素
  XMLElement* xml_root = doc.RootElement();
  if (!xml_root) {
    KERROR("Load XML failed: %s, unable find root.", filename);
    return;
  }

  root_.Read(xml_root);
}

const XmlNode& XmlReader::operator[](const char* key) const {
  return root_.operator[](key);
}

const XmlNode& XmlReader::Get(const char* key) const { return root_.Get(key); }

std::wstring XmlValue::WString() const {
  WCHAR buffer[512];
  MultiByteToWideChar(CP_ACP, 0, value, -1, buffer, 512);
  return std::wstring(buffer);
}

XmlValue::operator std::string() const {
  if (value == nullptr) {
    return std::string();
  } else {
    return std::string(value);
  }
}

XmlValue::operator std::string_view() const {
  if (value == nullptr) {
    return EMPTY_STRING;
  } else {
    return std::string_view(value);
  }
}

XmlValue::operator eastl::string() const {
  if (value == nullptr) {
    return EASTL_EMPTY_STRING;
  } else {
    return eastl::string(value);
  }
}

XmlValue::operator eastl::string_view() const {
  if (value == nullptr) {
    return EASTL_EMPTY_STRING_VIEW;
  } else {
    return eastl::string_view(value);
  }
}

XmlValue::operator sstr() const {
  if (value == nullptr) {
    return EMPTY_STATIC_STRING;
  } else {
    return sstr(value);
  }
}

XmlValue::operator XMFLOAT4() const {
  XMFLOAT4 f = {};
  if (value == nullptr) {
    return f;
  }
  sscanf(value, "%f,%f,%f,%f", &f.x, &f.y, &f.z, &f.w);
  return f;
}

XmlValue::operator XMFLOAT3() const {
  XMFLOAT3 f = {};
  if (value == nullptr) {
    return f;
  }
  sscanf(value, "%f,%f,%f", &f.x, &f.y, &f.z);
  return f;
}

XmlValue::operator XMVECTOR() const {
  XMFLOAT3 f = {};
  if (value == nullptr) {
    return XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
  }
  sscanf(value, "%f,%f,%f", &f.x, &f.y, &f.z);
  return XMLoadFloat3(&f);
}

XmlValue::operator XMFLOAT2() const {
  XMFLOAT2 f = {};
  if (value == nullptr) {
    return f;
  }
  sscanf(value, "%f,%f", &f.x, &f.y);
  return f;
}

XmlValue::operator XMINT4() const {
  XMINT4 f = {};
  if (value == nullptr) {
    return f;
  }
  sscanf(value, "%d,%d,%d,%d", &f.x, &f.y, &f.z, &f.w);
  return f;
}

XmlValue::operator XMINT3() const {
  XMINT3 f = {};
  if (value == nullptr) {
    return f;
  }
  sscanf(value, "%d,%d,%d", &f.x, &f.y, &f.z);
  return f;
}

XmlValue::operator XMINT2() const {
  XMINT2 f = {};
  if (value == nullptr) {
    return f;
  }
  sscanf(value, "%d,%d", &f.x, &f.y);
  return f;
}

XmlValue::operator Rect() const {
  Rect f = {};
  if (value == nullptr) {
    return f;
  }
  sscanf(value, "%d,%d,%d,%d", &f.left, &f.top, &f.right, &f.bottom);
  return f;
}

XmlValue::operator Point() const {
  Point f = {};
  if (value == nullptr) {
    return f;
  }
  sscanf(value, "%d,%d", &f.x, &f.y);
  return f;
}

XmlValue::operator Size() const {
  Size f = {};
  if (value == nullptr) {
    return f;
  }
  sscanf(value, "%d,%d", &f.width, &f.height);
  return f;
}

XmlValue::operator RectF() const {
  RectF f = {};
  if (value == nullptr) {
    return f;
  }
  sscanf(value, "%f,%f,%f,%f", &f.left, &f.top, &f.right, &f.bottom);
  return f;
}

XmlValue::operator PointF() const {
  PointF f = {};
  if (value == nullptr) {
    return f;
  }
  sscanf(value, "%f,%f", &f.x, &f.y);
  return f;
}

XmlValue::operator SizeF() const {
  SizeF f = {};
  if (value == nullptr) {
    return f;
  }
  sscanf(value, "%f,%f", &f.width, &f.height);
  return f;
}

XmlValue::operator size_t() const { return int_value; }

void XmlValue::Release() {
  if (value != nullptr) {
    free(value);
    value = nullptr;
  }
}

} // namespace ikit